import math
import random
import res.aigame_config as config


# sigmod 函数
def sigmod(x):
    if x <= -700:
        return 0.0
    elif x >= 50:
        return 1.0
    y = 1 / (1 + math.exp(-x))
    return y


# 随机-1~1之间的值
def random_weight():
    return random.random()*2-1


# 异常测试类
class NeuralErr(object):
    pass


# 神经元
class Neuron(object):
    def __init__(self, pre_neuron_num):

        # 根据上一层神经元的数量来初始化weights
        # 初始化的weights应该选什么值
        # 选择标准  = 权值 * 输入数据 的累计和尽量处于活跃状态（-5，5）
        # 这里我们就将权值设定在（0，-1）
        self.weights = [random_weight() for _ in range(pre_neuron_num)]  # 权重
        self.value = 0  # 数值

    def cal_result(self, inputs):
        if not len(inputs) == len(self.weights):
            raise NeuralErr("输入数据的个数不正确")
        sum = 0
        for i in range(len(inputs)):
            sum += inputs[i] * self.weights[i]
        self.value = sigmod(sum)  # 选择标准  = 权值 * 输入数据 的累计和
        return self.value


# 隐藏节点
class Layer(object):
    def __init__(self, neuron_num, pre_neuron_num):
        self.neurons = [Neuron(pre_neuron_num) for i in range(neuron_num)]

    def __iter__(self):
        for n in self.neurons:
            yield n

    def __len__(self):
        return len(self.neurons)

# 神经网络
# network = [5, [5, 3, 2], 3]
'''
输入input: 5
隐藏层hiddens: 5 
输出结果output: 1
'''
class NuralNetwork(object):
    def __init__(self, input, hiddens, output):
        self.layers = []
        pre_layer_neurons = 0
        # 1. 输入层
        input_layer = Layer(input, pre_layer_neurons)
        self.layers.append(input_layer)
        # 2. 隐含层
        pre_layer_neurons = len(input_layer)
        for hidden in hiddens:
            hidden_layer = Layer(hidden, pre_layer_neurons)
            self.layers.append(hidden_layer)
            pre_layer_neurons = len(hidden_layer)
        # 3. 输出层
        output_layer = Layer(output, pre_layer_neurons)
        self.layers.append(output_layer)

    # 用于输入测试数据
    def getResult(self, inputs):
        if not len(inputs) == len(self.layers[0]):
            raise NeuralErr("提供的数据和输入层节点数不匹配")
        pre_values = []
        for layer in self.layers:
            result = []
            # 输入层获取感知数据
            if layer == self.layers[0]:  # 判断是否是第一层
                for i in range(len(inputs)):  # 判断输入长度是否与第一层数据长度相同
                    layer.neurons[i].value = inputs[i]
                    pre_values.append(layer.neurons[i].value)
            else:
                for neuron in layer:
                    neuron.cal_result(pre_values)
                    result.append(neuron.value)
                pre_values = result
        return result


    def getNetwork(self):
        '''
        将神经网络模型数据化
        1. 结构
        2. 数据集
        :return: {"network":[5,5,1],"weights":[w1,w2,w3,w4,w5...]} 5*5+5 = 30个权重值
        '''

        data = {"network":[], "weights":[]}
        for layer in self.layers:
            data['network'].append(len(layer))
            for neuron in layer:
                for weight in neuron.weights:
                    data['weights'].append(weight)
        return data


    def setNetwork(self, networkData):
        '''
        使用数据化模型重置神经网络
        :param networkData:
        :return:
        '''

        self.layers = []

        pre_layer_neurons = 0
        weight_index = 0
        for layer_neuron_num in networkData['network']:
            layer = Layer(layer_neuron_num, pre_layer_neurons)
            for neuron in layer:
                for i in range(len(neuron.weights)):
                    neuron.weights[i] = networkData['weights'][weight_index]
                    weight_index += 1
            self.layers.append(layer)
            pre_layer_neurons = layer_neuron_num

    def __str__(self):
        s = ""
        for layer in self.layers:
            s += "=" * 50 + "\n"
            for n in layer:
                s += "{"
                for w in n.weights:
                    s += str(w) + ""
                s += "}\n"
        return s



if __name__ == '__main__':
    # myNetwork = NuralNetwork(network[0], network[1], network[2])
    # print(myNetwork)
    # print("result===", myNetwork.getResult([1, 1, 1, 1, 1]))
    myNetwork1 = NuralNetwork(config.network[0], config.network[1], config.network[2])
    print(myNetwork1)
    myNetwork2 = NuralNetwork(config.network[0], config.network[1], config.network[2])
    print(myNetwork2)
    my = myNetwork1.getNetwork()
    myNetwork2.setNetwork(my)
    print(myNetwork2)




